<?php

namespace ESign\Http;

use ESign\Config;
use ESign\District\Common;
use ESign\District\District;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

class Request {

	public $header;

	/**
	 * @var Config $config
	 */
	private $config;

	/**
	 * @var District $district
	 */
	private $district;

	public function request(Config $config, District $district) {
		$this->config   = $config;
		$this->district = $district;

		$this->buildHeader();

		$res = $this->district->getResponse();

		try {
			$client = new Client([
				'verify'      => false,
				'http_errors' => false,
			]);

			$url   = $district->getUrl();
			$param = $district->getParameter();

			switch ($district->getMethod()) {
			case 'PUT':
				$response = $client->put($url, [
					'headers' => $this->header,
					'body'    => $this->district->getParameter()[0],
				]);
				break;
			case 'POST':
				$routeArr    = explode('/', $this->district->getRoute());
				$routeEndStr = end($routeArr);
				if ($routeEndStr === 'updateByThirdId') {
					$url = $url . '?thirdPartyUserId=' . $param['thirdPartyUserId'];
				}
				// var_dump($url);die;
				$response = $client->post($url, [
					'headers' => $this->header,
					'body'    => json_encode($param, JSON_UNESCAPED_SLASHES),
				]);
				// var_dump($url, [
				// 	'headers' => $this->header,
				// 	'body'    => json_encode($param, JSON_UNESCAPED_SLASHES),
				// ]);
				// // die;
				// var_dump($param);die;
				// var_dump($response->getBody());die;
				// var_dump($response->getBody()->getContents());die;
				break;
			case 'DELETE':
				$routeArr    = explode('/', $this->district->getRoute());
				$routeEndStr = end($routeArr);
				// var_dump($routeEndStr);die;
				if ($routeEndStr === 'deleteByThirdId') {
					$url = $url . '?thirdPartyUserId=' . $param['thirdPartyUserId'];
				}
				// var_dump($url);die;
				$response = $client->delete($url, [
					'headers' => $this->header,
					'body'    => json_encode($param, JSON_UNESCAPED_SLASHES),
				]);
				// var_dump($url, [
				// 	'headers' => $this->header,
				// 	'body'    => json_encode($param, JSON_UNESCAPED_SLASHES),
				// ]);
				// // die;
				// var_dump($param);die;
				// var_dump($response->getBody());die;
				// var_dump($response->getBody()->getContents());die;
				break;
			default: //get

				$routeArr    = explode('/', $this->district->getRoute());
				$routeEndStr = end($routeArr);
				// var_dump($routeEndStr);die;
				if ($routeEndStr === 'executeUrl') {
					unset($param['flowId']);
				}
				if ($routeEndStr === 'documents') {
					unset($param['flowId']);
				}
				if ($param) {
					$url .= '?' . http_build_query($param);
				}
				// var_dump($url, ['headers' => $this->header]);die;
				$response = $client->get($url, [
					'headers' => $this->header,
				]);
				// var_dump($response);die;
				break;
			}
			// var_dump([$response->getStatusCode(), $response->getBody()->getContents(), $response->getHeaders()]);die;
			return (new $res())
				->setCode($response->getStatusCode())
				->setBody($response->getBody()->getContents())
				->setHeader($response->getHeaders())->resolve();
		} catch (GuzzleException $e) {
			// var_dump($e->getMessage());die;
			return (new $res())
				->setCode('500')
				->setError($e->getMessage());
		}

	}

	/**
	 * 生成header
	 */
	public function buildHeader() {
		if ($this->district->getHeader()) {
			$this->header = $this->district->getHeader();
			return;
		}
		$this->header = [
			'X-Tsign-Open-App-Id'       => $this->config->getAppId(),
			'Content-Type'              => 'application/json; charset=UTF-8',
			'X-Tsign-Open-Ca-Timestamp' => self::getMillisecond(),
			'Accept'                    => '*/*',
			'X-Tsign-Open-Auth-Mode'    => 'Signature',
		];
		$noMd5 = 1;
		if ($this->district->getMethod() == District::$FORM) {
			$noMd5 = 0;
		}
		if ($this->district->getMethod() == District::$GET) {
			$noMd5 = 0;
		}
		if ($noMd5) {
			$this->header['Content-MD5'] = Common::base64Md5($this->district->getParameter());
		}
		// var_dump($this->header['Content-MD5']);die;
		$this->header['X-Tsign-Open-Ca-Signature'] = $this->getSignature($this->header['Content-MD5'] ?? '');
		// var_dump($this->header);die;
	}

	private function getSignature($contentMd5 = ''): string{
		$signArray = [
			//HTTPMethod
			$this->district->getMethod(),
			//Accept
			"*/*",
		];
		//Content-MD5
		$signArray[] = $contentMd5;

		//Content-Type
		$signArray[] = 'application/json; charset=UTF-8';
		//Date
		$signArray[] = "";
		//Url
		// var_dump($this->district->getParameter());die;

		$isGet = 0;
		if ($this->district->getMethod() == District::$FORM) {
			$isGet = 1;
		}
		if ($this->district->getMethod() == District::$GET) {
			$isGet = 1;
		}

		$paramStr = '';
		$paramArr = $this->district->getParameter();
		// var_dump($paramArr);die;
		// 根据/拆分数据判断最后一个是不是executeUrl获取签署地址，如果是的话去掉flowId
		// var_dump($this->district->getRoute());die;
		$routeArr    = explode('/', $this->district->getRoute());
		$routeEndStr = end($routeArr);
		// var_dump($routeEndStr);die;
		if ($routeEndStr === 'executeUrl') {
			unset($paramArr['flowId']);
		}
		if ($routeEndStr === 'documents') {
			unset($paramArr['flowId']);
		}
		// if ($routeEndStr === 'updateByThirdId') {
		// 	unset($paramArr['thirdPartyUserId']);
		// }

		if ($isGet && $paramArr) {
			$sortArr = array_keys($paramArr);
			// var_dump(array_keys($paramArr));die;
			sort($sortArr);
			// var_dump($sortArr);die;
			foreach ($sortArr as $key => $value) {
				$paramStr .= $value . '=' . $paramArr[$value] . '&';
			}
			$paramStr = rtrim($paramStr, '&');
			// var_dump($paramStr);die;
			$signArray[] = $this->district->getRoute() . '?' . $paramStr;
		} else {
			if ($routeEndStr === 'updateByThirdId') {
				$signArray[] = $this->district->getRoute() . '?thirdPartyUserId=' . $paramArr['thirdPartyUserId'];
			} elseif ($routeEndStr === 'deleteByThirdId') {
				$signArray[] = $this->district->getRoute() . '?thirdPartyUserId=' . $paramArr['thirdPartyUserId'];
			} else {
				$signArray[] = $this->district->getRoute();
			}
		}

		// var_dump($signArray);die;
		$stringToSign = implode("\n", $signArray);

		// var_dump($stringToSign);exit;

//        $stringToSign = $this->district->getMethod() . "\n" . '*/*' . "\n" . $contentMd5 . "\n" . 'application/json; charset=UTF-8' . "\n" . '' . "\n";
		//        $stringToSign .= $this->district->getUrl();

		$signature = hash_hmac("sha256", utf8_encode($stringToSign), utf8_encode($this->config->getSecret()), true);

		return base64_encode($signature);
	}

	/**
	 * 毫秒
	 * @return string
	 */
	public static function getMillisecond(): string {
		list($s1, $s2) = explode(' ', microtime());
		return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
	}
}
